using System;
using System.IO;
using System.Net;
using Microsoft.Win32;
using System.Web.Services;
using System.Reflection;
using System.Xml;
using System.Xml.XPath;
using System.Web.Services.Protocols;

using System.Runtime.InteropServices;
using System.Globalization;
using System.Security.Principal;
using Microsoft.Crm;
using Microsoft.Crm.Sdk.Proxy;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CRM_Perf_BenchMark
{
    /// <summary>
    /// Summary description for SyncToOutlook
    /// </summary>
    [TestClass]
    public class SyncToOutlook
    {
       
        CRMEntity user = null;
        private string crmServer;
        private string m_ApiServer;

        XmlDocument resultsDoc = null;
        MySyncDataRecorder sdr = null;

        // Contact fields
        const string c_wzContactColumnSetInsert = "<columnset>" +
        "<column>contactid</column>" +
        "<column>parentcustomerid</column>" +
        "<column>address1_city</column>" +
        "<column>address1_country</column>" +
        "<column>address1_line1</column>" +
        "<column>address1_line2</column>" +
        "<column>address1_line3</column>" +
        "<column>address1_postalcode</column>" +
        "<column>address1_postofficebox</column>" +
        "<column>address1_stateorprovince</column>" +
        "<column>anniversary</column>" +
        "<column>assistantname</column>" +
        "<column>assistantphone</column>" +
        "<column>birthdate</column>" +
        "<column>childrensnames</column>" +
        "<column>department</column>" +
        "<column>description</column>" +
        "<column>emailaddress1</column>" +
        "<column>emailaddress2</column>" +
        "<column>emailaddress3</column>" +
        "<column>fax</column>" +
        "<column>firstname</column>" +
        "<column>ftpsiteurl</column>" +
        "<column>governmentid</column>" +
        "<column>jobtitle</column>" +
        "<column>lastname</column>" +
        "<column>managername</column>" +
        "<column>middlename</column>" +
        "<column>mobilephone</column>" +
        "<column>nickname</column>" +
        "<column>ownerid</column>" +
        "<column>pager</column>" +
        "<column>salutation</column>" +
        "<column>spousesname</column>" +
        "<column>statuscode</column>" +
        "<column>suffix</column>" +
        "<column>telephone1</column>" +
        "<column>telephone2</column>" +
        "<column>telephone3</column>" +
        "<column>websiteurl</column>" +
        "<column>yomifirstname</column>" +
        "<column>yomilastname</column>" +
        "<column>yomimiddlename</column>" +
        "</columnset>";

        const string c_wzContactColumnSetDelete = "<columnset><column>contactid</column></columnset>";

        // Appointment fields
        const string c_wzApptColumnSetInsert = "<columnset>" +
        "<column>activityid</column>" +
        "<column>location</column>" +
        "<column>scheduledstart</column>" +
        "<column>regardingobjectid</column>" +
        "<column>ownerid</column>" +
        "<column>prioritycode</column>" +
        "<column>subject</column>" +
        "<column>scheduledend</column>" +
        "<column>statuscode</column>" +
        "<column>description</column>" +
        "<column>isalldayevent</column>" +
        "<column>outlookownerapptid</column>" +
        "<column>globalobjectid</column>" +
        "<column>requiredattendees</column>" +
        "<column>optionalattendees</column>" +
        "<column>organizer</column>" +
        "</columnset>";

        const string c_wzApptColumnSetDelete = "<columnset><column>activityid</column></columnset>";

        // Service Appointment fields
        const string c_wzServiceApptColumnSetInsert = "<columnset>" +
        "<column>activityid</column>" +
        "<column>location</column>" +
        "<column>scheduledstart</column>" +
        "<column>regardingobjectid</column>" +
        "<column>ownerid</column>" +
        "<column>prioritycode</column>" +
        "<column>subject</column>" +
        "<column>scheduledend</column>" +
        "<column>statuscode</column>" +
        "<column>description</column>" +
        "<column>isalldayevent</column>" +
        "<column>serviceid</column>" +
        "<column>resources</column>" +
        "<column>customers</column>" +
        "</columnset>";

        const string c_wzServiceApptColumnSetDelete = "<columnset><column>activityid</column></columnset>";

        // Task fields
        const string c_wzTaskColumnSetInsert = "<columnset>" +
        "<column>activityid</column>" +
        "<column>description</column>" +
        "<column>ownerid</column>" +
        "<column>regardingobjectid</column>" +
        "<column>scheduleddurationminutes</column>" +
        "<column>scheduledend</column>" +
        "<column>scheduledstart</column>" +
        "<column>prioritycode</column>" +
        "<column>statecode</column>" +
        "<column>statuscode</column>" +
        "<column>subject</column>" +
        "<column>percentcomplete</column>" +
        "</columnset>";

        const string c_wzTaskColumnSetDelete = "<columnset><column>activityid</column></columnset>";



        int DELETED_ITEMS_BATCH_SIZE = 100;
        int INSERTED_ITEMS_BATCH_SIZE = 100;	// TODO:  DISCOVER OPTIMUM VALUES FOR THESE
        
        static string c_wzTagIdContact = "contactid";
        static string c_wzTagIdActivity = "activityid";

        static string c_wzPlatformNameContact = "Contact";
        static string c_wzPlatformNameTask = "Task";
        static string c_wzPlatformNameAppt = "Appointment";
        static string c_wzPlatformNameServiceAppt = "ServiceAppointment";

        // NOTE:  THE ORDER OF THIS ARRAY MUST MATCH THE ORDER OF THE ENTITY_NAMES ENUM!
        static string[] s_EntityNamesForSync = {
									c_wzPlatformNameContact,
									c_wzPlatformNameTask,
									c_wzPlatformNameAppt,
									c_wzPlatformNameServiceAppt};
        string userName = string.Empty;
        string domain = string.Empty;
        string m_crmticket = string.Empty;
        string clientKeyPath = string.Empty;
        NetworkCredential nc = null;
        private ICredentials networkCredential = null;
        CrmService _service = null;
        private String clientId = null;
        private String subscriptionId = null;
      
        
        private RegistryKey regKey = null;
        string outDir = string.Empty;

        // Need this public property to don't dependent on RegControl in unit test, perf driver, etc.
        public String ClientKeyPath
        {
            get
            {
                if (clientKeyPath == null)
                    clientKeyPath = "SOFTWARE\\Microsoft\\MSCRMClient";

                return clientKeyPath;
            }

            set { clientKeyPath = value; }
        }

        // Need this public property for CreateSubscription so that we do not use the Environemnt variable for user and domain
        public String UserName
        {
            get
            {
                if (userName == null)
                    userName = Environment.UserName;

                return userName;
            }

            set { userName = value; }
        }

        // Need this public property for CreateSubscription so that we do not uase the Environemnt variable for user and domain
        public String UserDomain
        {
            get
            {
                if (domain == null)
                    domain = Environment.UserDomainName;

                return domain;
            }

            set { domain = value; }
        }

        // Need this public property for webservice so that we do not use the default credential
        public System.Net.ICredentials NetworkCredential
        {
            get
            {
                if (networkCredential == null)


                    return System.Net.CredentialCache.DefaultCredentials;
                else
                    return networkCredential;

            }

            set { networkCredential = value; }
        }

        private RegistryKey RegKey
        {
            get
            {
                if (this.regKey == null)
                {
                    this.regKey = Registry.CurrentUser.OpenSubKey(ClientKeyPath, true);
                    if (this.regKey == null)
                        throw new Exception("Client registry subkey not found in the registry");
                }
                return this.regKey;
            }
        }

        private String SubscriptionId
        {
            get
            {
                if (this.subscriptionId == null)
                {
                    subscriptionId = (String)RegKey.GetValue("OutlookSyncSubscriptionId");
                }
                return this.subscriptionId;
            }
            set
            {
                subscriptionId = value;
                RegKey.SetValue("OutlookSyncSubscriptionId", subscriptionId);
            }
        }

        private String ClientId
        {
            get
            {
                if (this.clientId == null)
                {
                    clientId = (String)RegKey.GetValue("OutlookSyncClientId");
                }
                return this.clientId;
            }
            set
            {
                clientId = value;
                RegKey.SetValue("OutlookSyncClientId", clientId);
            }
        }

        protected CrmService SyncService
        {
            get
            {
                if (_service == null)
                {
                    _service = new CrmService();
                    _service.Url = m_ApiServer + "/MSCRMServices/2007/CrmService.asmx";

                    _service.UnsafeAuthenticatedConnectionSharing = true;
                    _service.Credentials = NetworkCredential;
                    _service.ConnectionGroupName = user["domainname"];
                    _service.Timeout = 1000000;

                    CrmAuthenticationToken token = new CrmAuthenticationToken();
                    token.CallerId = new Guid(user[EntityIDNames.User]);
                    token.OrganizationName = user["organizationname"];                    
                    token.AuthenticationType = AuthenticationType.AD;
                    
                    _service.CrmAuthenticationTokenValue = token;
                }
                return _service;
            }
        }

        private TestContext testContext;

        public SyncToOutlook()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        [ClassInitialize()]
        public static void MyClassInitialize(TestContext testContext)
        {
            //this is the directory where all perf results are stored
            string outlookUnitTestDir = Directory.GetDirectoryRoot(Directory.GetCurrentDirectory()) + @"outlooksyncdata\SyncToOutlook";
            if (!Directory.Exists(outlookUnitTestDir))
                Directory.CreateDirectory(outlookUnitTestDir);
        }

        /// <summary>
        /// Initialize() is called once during test execution before
        /// test methods in this test class are executed.
        /// </summary>
        [TestInitialize()]
        public void Initialize()
        {
            System.Diagnostics.Trace.WriteLine("init" + DateTime.Now.ToString());

			bool foundUser = false;

			while (!foundUser)
			{
				try
				{
					user = EntityManager.Instance.GetNextOutlookUser();
					foundUser = true;
				}
				catch (Exception ex)
				{
					//we could not get a user
					System.Diagnostics.Trace.WriteLine(ex.ToString());
					//lets sleep 5 seconds befor we try again
					System.Threading.Thread.Sleep(5000);
				}
			}

            try
            {
                crmServer = user["CrmServer"];
                m_ApiServer = user["ApiServer"];

                string[] splits = user["domainname"].Split(new char[] { '\\' }, 2, StringSplitOptions.RemoveEmptyEntries);
                if (splits.Length > 1)
                {
                    domain = splits[0];
                    userName = splits[1];
                }
                else
                {
                    string[] passportusersplit = user["passportname"].Split(new char[] { '@' }, 2, StringSplitOptions.RemoveEmptyEntries);
                    userName = passportusersplit[0];
                }

                //we know the user Name and passowrd, we need to get 
                //we are adding org id because we going to run tests for multiple orgs
                //user names can be same for different orgs
                clientKeyPath = "SOFTWARE\\Microsoft\\MSCRM" + userName + "_" + user["organizationid"];
                nc = new System.Net.NetworkCredential(userName, user["userpassword"], domain);
                NetworkCredential = nc;

                //we are going to see here if registry exists, if it does not then we are going to create the registry
                CreateRegistryForUser(nc);

                sdr = new MySyncDataRecorder();
                // create transfer data provider
                resultsDoc = new XmlDocument();
                resultsDoc.AppendChild(resultsDoc.CreateElement("run_results"));
            }

            catch (System.Web.Services.Protocols.SoapException ex)
            {
                System.Diagnostics.Trace.WriteLine("SyncToOutlook:User " + userName + " Soap Exception Detail:" + ex.Detail.InnerXml);
                throw ex;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine("SyncToOutlook:User " + userName + ":Exception Message:" + ex.Message);
                throw ex;
            }
        }

        /// <summary>
        /// Cleanup() is called once during test execution after
        /// test methods in this class have executed unless the
        /// corresponding Initialize() call threw an exception.
        /// </summary>
        [TestCleanup()]
        public void Cleanup()
        {
			if (user == null)
				return;
            System.Diagnostics.Trace.WriteLine("SyncToOutlook:cleanup" + DateTime.Now.ToString());

			EntityManager.Instance.FreeOutlookUser(user);
            
                try
                {
                    XmlDocument docNode = resultsDoc.DocumentElement.OwnerDocument;
                    XmlElement el;
                    el = sdr.SerializeToXML(resultsDoc.DocumentElement.OwnerDocument);
                    resultsDoc.DocumentElement.AppendChild(el);


                    string fileName = userName + "_" + "SyncToOutlook" + "_" + DateTime.Now.ToString() + ".xml";
                    fileName = fileName.Replace(" ", "_");
                    fileName = fileName.Replace("/", "_");
                    fileName = fileName.Replace(":", "_");
                    System.Diagnostics.Trace.WriteLine(fileName);
                    
                    resultsDoc.Save(ConfigSettings.Default.OutlookSyncDir + "\\SyncToOutlook\\" + fileName);
                }
                catch { }
        }

        [TestMethod]
        public void SyncToOutlookTM()
        {
			if (user == null)
				return;

            try
            {
                DateTime dtStart = DateTime.Now;
                DateTime dtFinish = DateTime.Now;

                if (SubscriptionId == null)
                {
                    testContext.BeginTimer("SyncToOutlook:CreateSubscription");
                    dtStart = DateTime.Now;
                    CreateOnlineSubscription();
                    dtFinish = DateTime.Now;
                    testContext.EndTimer("SyncToOutlook:CreateSubscription");

                    if (sdr != null)
                    {
                        sdr.CreateSubscription = dtFinish - dtStart;
                    }
                }

                // Step2 : PrepareSync
                testContext.BeginTimer("SyncToOutlook:DoPrepareSync");
                dtStart = DateTime.Now;
                String syncInfo = PrepareOnlineSync();
                dtFinish = DateTime.Now;
                testContext.EndTimer("SyncToOutlook:DoPrepareSync");
                if (sdr != null)
                {
                    sdr.PrepareSync = dtFinish - dtStart;
                    sdr.SyncInfo = syncInfo;
                }

                // Step2 : GetData
                System.Diagnostics.Trace.WriteLine(syncInfo);
                
                XmlDocument doc = CreateXmlDocument(syncInfo);
                XPathNavigator nav = doc.CreateNavigator();
                XPathNodeIterator iterator = nav.Select("/result/entity");

                SyncInfo[] syncData = new SyncInfo[iterator.Count];

                int total = 0;
                testContext.BeginTimer("SyncToOutlook:GetData");
                dtStart = DateTime.Now;
                int numEntities = GetData(iterator, ref syncData, ref total);
                dtFinish = DateTime.Now;
                testContext.EndTimer("SyncToOutlook:GetData");
                
                if (total == 0)
                    return;

                for (int j = 0; j < numEntities; j++)
                {
                    switch (syncData[j].entityName.ToLower())
                    {
                        case "account":
                            Console.WriteLine("Syncing accounts");
                            PostSyncAccounts(syncData[j], SubscriptionId, c_wzTagIdContact, c_wzContactColumnSetInsert, c_wzContactColumnSetDelete);
                            break;

                        case "contact":
                            testContext.BeginTimer("SyncToOutlook:SyncItemByTypeForContact");
                            SyncItemByType(syncData[j], SubscriptionId, c_wzTagIdContact, c_wzContactColumnSetInsert, c_wzContactColumnSetDelete);
                            testContext.EndTimer("SyncToOutlook:SyncItemByTypeForContact");
                            break;

                        case "task":
                            testContext.BeginTimer("SyncToOutlook:SyncItemByTypeForTask");
                            SyncItemByType(syncData[j], SubscriptionId, c_wzTagIdActivity, c_wzTaskColumnSetInsert, c_wzTaskColumnSetDelete);
                            testContext.EndTimer("SyncToOutlook:SyncItemByTypeForTask");
                            break;
                
                        case "appointment":
                            testContext.BeginTimer("SyncToOutlook:SyncItemByTypeForAppt");
                            SyncItemByType(syncData[j], SubscriptionId, c_wzTagIdActivity, c_wzApptColumnSetInsert, c_wzApptColumnSetDelete);
                            testContext.EndTimer("SyncToOutlook:SyncItemByTypeForAppt");
                            break;
                       
                        case "serviceappointment":
                            testContext.BeginTimer("SyncToOutlook:SyncItemByTypeForServiceAppt");
                            SyncItemByType(syncData[j], SubscriptionId, c_wzTagIdActivity, c_wzServiceApptColumnSetInsert, c_wzServiceApptColumnSetDelete);
                            testContext.EndTimer("SyncToOutlook:SyncItemByTypeForServiceAppt");
                            break;

                        default:
                            break;
                    }
                }
                dtFinish = DateTime.Now;
                if (sdr != null)
                    sdr.MoveData = dtFinish - dtStart;

                System.Diagnostics.Trace.WriteLine("User:" + userName + " SyncEnd:" + DateTime.Now.ToString());
            }
            catch (SoapException e)
            {
                System.Diagnostics.Trace.WriteLine("SyncToOutlook:User:" + userName + " Begin Got SOAP exception");
                System.Diagnostics.Trace.WriteLine(e.Detail.InnerXml);
                System.Diagnostics.Trace.WriteLine("SyncToOutlook:User:" + userName + " End Got SOAP exception");
                throw e;
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine("SyncToOutlook:User:" + userName + " Begin Got exception");
                System.Diagnostics.Trace.WriteLine(e.ToString());
                System.Diagnostics.Trace.WriteLine("SyncToOutlook:User:" + userName + " End Got exception");
                throw e;
            }
        }

        private XmlDocument CreateXmlDocument(string xml)
        {
            XmlTextReader reader = new XmlTextReader(new StringReader(xml));

            reader.ProhibitDtd = true;
            reader.WhitespaceHandling = WhitespaceHandling.Significant;

            XmlDocument xmlDoc = new XmlDocument();

            // Set XmlResolver to null to prevent usage of the default XmlResolver.
            xmlDoc.XmlResolver = null;

            xmlDoc.Load(reader);
            reader.Close();

            return xmlDoc;
        }

        private int ConvertAttributeToInt(XmlAttribute attribute)
        {
            if (attribute == null)
                return 0;

            try
            {
                int count = Int32.Parse(attribute.Value);
                return count;
            }
            catch
            {
                throw new CrmArgumentException("Cannot convert attribute to integer.", attribute.Value);
            }
        }

        public void CreateOnlineSubscription()
        {
            string clientId = String.Empty;
            try
            {
                subscriptionclients subscrClient = new subscriptionclients();
                subscrClient.machinename = Environment.UserDomainName + "\\" + Environment.MachineName;

                CreateOutlookSubscriptionSubscriptionClientsRequest request = new CreateOutlookSubscriptionSubscriptionClientsRequest();
                request.Target = subscrClient;

                CreateOutlookSubscriptionSubscriptionClientsResponse response = (CreateOutlookSubscriptionSubscriptionClientsResponse)SyncService.Execute(request);

                // Actually it's a clientId
                Guid clientIdGuid = response.id;

                clientId = clientIdGuid.ToString("B").ToUpper();

                // Retrieve subscription id
                RetrieveClientSubscriptionSubscriptionClientsRequest reqRetrieve = new RetrieveClientSubscriptionSubscriptionClientsRequest();
                reqRetrieve.ClientId = clientIdGuid;
                ColumnSet columnSet = new ColumnSet();
                columnSet.Attributes = new string[] { "subscriptionid" };
                reqRetrieve.ColumnSet = columnSet;

                RetrieveClientSubscriptionSubscriptionClientsResponse respRetrieve = (RetrieveClientSubscriptionSubscriptionClientsResponse)SyncService.Execute(reqRetrieve);

                Guid subscriptionId = ((subscriptionclients)respRetrieve.BusinessEntity).subscriptionid.Value;

                ClientId = clientId;

                SubscriptionId = subscriptionId.ToString("B").ToUpper();

                Console.WriteLine("User:" + userName + " " + subscriptionId.ToString() + " " + DateTime.Now.ToString());

                if (IsPrimaryOnlineClient(clientId) == false)
                {
                    Console.WriteLine("User:" + userName + " Setting this primary client " + DateTime.Now.ToString());
                    SetPrimaryOnlineClient(clientId);
                }
            }
            catch (SoapException e)
            {
                throw e;
            }
        }
        /// <summary>
        /// Returns the value returned from the IsPrimaryOnlineClient API
        /// on the SyncService.
        /// </summary>
        /// <param name="clientId"></param>
        /// <returns></returns>
        public bool IsPrimaryOnlineClient(String clientId)
        {
            try
            {
                IsPrimaryClientSubscriptionClientsRequest request = new IsPrimaryClientSubscriptionClientsRequest();
                request.ClientId = new Guid(clientId);
                IsPrimaryClientSubscriptionClientsResponse response = (IsPrimaryClientSubscriptionClientsResponse)SyncService.Execute(request);
                return response.isPrimaryClient;
            }
            catch (SoapException e)
            {
                throw e;
            }
        }

        /// <summary>
        /// Invokes the SetPrimaryClient API on the SyncService.
        /// </summary>
        /// <param name="clientId"></param>
        public void SetPrimaryOnlineClient(String clientId)
        {
            try
            {
                SetPrimaryClientSubscriptionClientsRequest request = new SetPrimaryClientSubscriptionClientsRequest();
                request.ClientId = new Guid(clientId);
                SyncService.Execute(request);
            }
            catch (SoapException e)
            {
                throw e;
            }
        }

        /// <summary>
        /// Invokes Prepare Outlook Sync on the sync service.
        /// </summary>
        /// <param name="clientId"></param>
        /// <returns></returns>
        public String PrepareOnlineSync()
        {
            try
            {
                PrepareOutlookSyncSubscriptionClientsRequest request = new PrepareOutlookSyncSubscriptionClientsRequest();
                request.ClientId = new Guid(ClientId);
                PrepareOutlookSyncSubscriptionClientsResponse response = (PrepareOutlookSyncSubscriptionClientsResponse)SyncService.Execute(request);
                return response.SyncInfoXml;
            }
            catch (SoapException e)
            {
                throw e;
            }
        }

        /// <summary>
        /// Invokes the PostOutlookSync on the SyncService.
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="entityName"></param>
        /// <param name="syncAction"></param>
        /// <param name="batchSize"></param>
        public void PostOnlineSync(String clientId, String entityName, Microsoft.Crm.Sdk.Proxy.SyncAction syncAction, int batchSize)
        {
            try
            {
                PostOutlookSyncSubscriptionClientsRequest request = new PostOutlookSyncSubscriptionClientsRequest();
                request.ClientId = new Guid(clientId);
                request.BatchSize = batchSize;
                request.EntityName = entityName;
                request.SyncAction = syncAction;
                SyncService.Execute(request);
            }
            catch (SoapException e)
            {
                throw e;
            }
        }


        private int GetData(XPathNodeIterator iterator, ref SyncInfo[] syncData, ref int total)
        {   
            int insertCount = 0;
            int deleteCount = 0;

            int index = 0;
            while (iterator.MoveNext())
            {
                XmlNode node = ((IHasXmlNode)iterator.Current).GetNode();
                XmlAttributeCollection xmlAttributes = node.Attributes;

                String entityName = xmlAttributes["name"].Value;

                deleteCount = ConvertAttributeToInt(xmlAttributes["delete_count"]);
                insertCount = ConvertAttributeToInt(xmlAttributes["insert_count"]);
                syncData[index] = new SyncInfo();
                syncData[index].entityName = entityName;
                syncData[index].deleteCount = deleteCount;
                syncData[index].insertCount = insertCount;

                System.Diagnostics.Trace.WriteLine("Entity:" + syncData[index].entityName);
                System.Diagnostics.Trace.WriteLine("deleteCount:" + syncData[index].deleteCount);
                System.Diagnostics.Trace.WriteLine("insertCount:" + syncData[index].insertCount);

                index++;
                total += insertCount + deleteCount;
            }

            return index;
        }

        /// <summary>
        /// Invokes the GetOutlookSyncData on the SyncService.
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="entityName"></param>
        /// <param name="syncAction"></param>
        /// <param name="batchSize"></param>
        /// <param name="columnSetXml"></param>
        /// <returns></returns>
        public String GetOnlineData(String clientId, String entityName, SyncAction syncAction, int batchSize, String columnSetXml)
        {
            try
            {
                GetOutlookSyncDataSubscriptionClientsRequest request = new GetOutlookSyncDataSubscriptionClientsRequest();
                request.ClientId = new Guid(clientId);
                request.BatchSize = batchSize;
                request.ColumnSetXml = columnSetXml;
                request.EntityName = entityName;
                request.SyncAction = syncAction;
                GetOutlookSyncDataSubscriptionClientsResponse response = (GetOutlookSyncDataSubscriptionClientsResponse)SyncService.Execute(request);
                return response.SyncDataXml;
            }
            catch (SoapException e)
            {
                throw e;
            }
        }

        private void PostSyncAccounts(SyncInfo syncData, string id, string flag, string insertSet, string deleteSet)
        {
            int numberofItems = syncData.insertCount + syncData.deleteCount;

            Console.WriteLine("User:" + userName + " numberofItems:" + numberofItems + " " + DateTime.Now.ToString());
            if (numberofItems == 0)
                return;

            //bool moreRecords = true;
            if (syncData.deleteCount > 0)
            {
                PostOnlineSync(ClientId, syncData.entityName, SyncAction.Delete, syncData.deleteCount);
            }

            if (syncData.insertCount > 0)
            {
                PostOnlineSync(ClientId, syncData.entityName, SyncAction.Insert, syncData.insertCount);
            }
        }

        private void SyncItemByType(SyncInfo syncData, string id, string flag, string insertSet, string deleteSet)
        {
            int numberofItems = syncData.insertCount + syncData.deleteCount;

            System.Diagnostics.Trace.WriteLine("User:" + userName + " numberofItems:" + numberofItems + " " + DateTime.Now.ToString());
            if (numberofItems == 0)
                return;

            bool moreRecords = true;
            if (syncData.deleteCount > 0)
            {
                while (moreRecords)
                {
                    string deletedItems = GetOnlineData(ClientId, syncData.entityName, SyncAction.Delete, DELETED_ITEMS_BATCH_SIZE, deleteSet);
                    XmlDocument doc = CreateXmlDocument(deletedItems);
                    XmlNodeList delNodeList = doc.FirstChild.ChildNodes;

                    if (delNodeList.Count == 0)
                    {
                        moreRecords = false;
                        continue;
                    }

                    for (int i = 0; i < delNodeList.Count; i++)
                    {
                        XmlNode node = delNodeList[i];
                        //System.Diagnostics.Trace.WriteLine("Sleeping for 10msec for a delete");
                        //delete item from outlook
                        //System.Threading.Thread.Sleep(10);
                    }

                    PostOnlineSync(ClientId, syncData.entityName, SyncAction.Delete, DELETED_ITEMS_BATCH_SIZE);
                }
            }

            if (syncData.insertCount > 0)
            {
                moreRecords = true;
                while (moreRecords)
                {
                    string insertedItems = GetOnlineData(ClientId, syncData.entityName, SyncAction.Insert, INSERTED_ITEMS_BATCH_SIZE, insertSet);
                    XmlDocument doc = CreateXmlDocument(insertedItems);

                    XmlNodeList insertNodeList = doc.FirstChild.ChildNodes;

                    if (insertNodeList.Count == 0)
                    {
                        moreRecords = false;
                        continue;
                    }

                    for (int i = 0; i < insertNodeList.Count; i++)
                    {
                        XmlNode node = insertNodeList[i];
                        //System.Diagnostics.Trace.WriteLine("Sleeping for 150msec for an insert");
                        //insert item into outlook
                        //System.Threading.Thread.Sleep(100);
                    }
                    PostOnlineSync(ClientId, syncData.entityName, SyncAction.Insert, INSERTED_ITEMS_BATCH_SIZE);
                }
            }
        }

        private void CreateRegistryForUser(System.Net.NetworkCredential nc)
        {
            string userId = user["systemuserid"];
            string businessId = user["businessunitid"];

            RegistryKey userRegKey = Registry.CurrentUser.OpenSubKey(clientKeyPath, true);
            if (userRegKey == null)
                userRegKey = Registry.CurrentUser.CreateSubKey(clientKeyPath);

            //we need to create - BusinessId, UserId, ServerUrl
            userRegKey.SetValue("BusinessId", "{" + businessId + "}", RegistryValueKind.String);
            userRegKey.SetValue("UserId", "{" + userId + "}", RegistryValueKind.String);
            userRegKey.SetValue("ServerUrl", crmServer + "/MSCRMServices", RegistryValueKind.String);
        }

        public TestContext TestContext
        {
            set { testContext = value; }
            get { return testContext; }
        }
    }

    public class SyncInfo
    {
        public int insertCount;
        public int deleteCount;
        public string entityName;
    }

    public class MySyncDataRecorder
    {
        public TimeSpan CreateSubscription;
        public TimeSpan PrepareSync;
        public TimeSpan GetSyncInfo;
        public String SyncInfo;
        public TimeSpan SchemaChanges;
        public TimeSpan MoveData;

        public XmlElement SerializeToXML (XmlDocument xmld)
        {
            XmlElement el;
            XmlAttribute att;
            el = xmld.CreateElement ("Sync_Data");
            att = xmld.CreateAttribute ("CreateSubscription");
            att.Value = CreateSubscription.ToString();
            el.Attributes.Append (att);
            att = xmld.CreateAttribute ("PrepareSync");
            att.Value = PrepareSync.ToString();
            el.Attributes.Append (att);

            if(SyncInfo != null) 
            {
                int count = ParseSyncInfo(SyncInfo);
                att = xmld.CreateAttribute ("TotalEntity");
                att.Value = count.ToString();
                el.Attributes.Append (att);
                el.InnerXml = SyncInfo;
            }
            
            att = xmld.CreateAttribute ("MoveData");
            att.Value = MoveData.ToString();
            el.Attributes.Append (att);
            
            return (el);
        }
        
        //this is to get total number of records
        public int ParseSyncInfo(string SyncInfo) 
        {
            int count = 0;
            string nl = System.Environment.NewLine;
            string [] lines = SyncInfo.Split(new char [] {'/'});
      
            for(int i = 0; i < lines.Length -1; i++) 
            {
                //we need to get insert_count from each line
                string [] equals = lines[i].Split(new char [] {'='});
        
                if(equals.Length == 3) 
                {
                    string countS = equals[2];
                    countS = countS.Replace("\"", "");
                    count = count + Convert.ToInt32(countS);
                }
                else 
                {
                    if(equals.Length > 3) 
                    {
                        //we might have delete count too
                        string countS = equals[2];
                        countS = countS.Replace("\"", "");
                        string [] insertCountS = countS.Split(new char[] {' '});
                        countS = insertCountS[0];
                        System.Diagnostics.Trace.WriteLine("countS:" + countS);
                        count = count + Convert.ToInt32(countS);
                        
                        //insert count
                        countS = equals[3];
                        countS = countS.Replace("\"", "");
                        count = count + Convert.ToInt32(countS);
                    }
                }
            }
            return count;
        }
    }
}
